/**
 * 监控预约任务服务
 *
 * 负责管理监控发现的预约任务，实现监控与预约分离
 */

import type { PrismaClient } from '@prisma/client'
import { getDatabase } from '../db/index'
import { createContextLogger } from '../logger/index'
import { LogCategory } from '../logger/types'

/**
 * 扩展的 Prisma 客户端类型，支持监控预约任务表的操作
 * 在某些环境下 Prisma 可能无法正常工作，因此需要回退到原生 SQL
 */
type ExtendedPrismaClient = PrismaClient & {
  monitoring_booking_task?: {
    findFirst: (args: any) => Promise<MonitoringBookingTask | null>
    findMany: (args: any) => Promise<MonitoringBookingTask[]>
    findUnique: (args: any) => Promise<MonitoringBookingTask | null>
    create: (args: any) => Promise<MonitoringBookingTask>
    update: (args: any) => Promise<MonitoringBookingTask>
    count: (args: any) => Promise<number>
  }
}

export interface CreateMonitoringBookingTaskInput {
  monitor_id: number
  appointment_id: number
  available_date: string // YYYY-MM-DD
  office_code: string
  service_type: string
  priority?: number
  booking_mode?: 'playwright' | 'request'
  booking_options?: any
  max_retries?: number
}

export interface MonitoringBookingTask {
  id: number
  monitor_id: number
  appointment_id: number
  available_date: string
  office_code: string
  service_type: string
  priority: number
  status: 'pending' | 'processing' | 'completed' | 'failed' | 'cancelled'
  booking_mode: string
  booking_options: string | null
  error_message: string | null
  retry_count: number
  max_retries: number
  created_at: Date
  updated_at: Date
  started_at: Date | null
  completed_at: Date | null
}

class MonitoringBookingTaskService {
  private db: ExtendedPrismaClient
  private logger = createContextLogger({ component: 'MonitoringBookingTaskService', category: LogCategory.MONITOR })

  constructor() {
    // 类型断言：getDatabase() 返回的 PrismaClient 在某些环境下可能需要回退到原生 SQL
    // 因此使用扩展类型来支持两种操作方式
    this.db = getDatabase() as unknown as ExtendedPrismaClient
  }

  /**
   * 创建监控预约任务
   */
  async createTask(input: CreateMonitoringBookingTaskInput): Promise<MonitoringBookingTask> {
    try {
      // 检查是否已存在相同任务（防止重复创建）
      // 注意：在某些打包环境下 Prisma 可能无法正常工作，会回退到原生 SQL
      const existing = this.db.monitoring_booking_task
        ? await this.db.monitoring_booking_task.findFirst({
            where: {
              appointment_id: input.appointment_id,
              available_date: input.available_date,
              status: {
                in: ['pending', 'processing'],
              },
            },
          })
        : null

      if (existing) {
        this.logger.debug('任务已存在，跳过创建', {
          appointment_id: input.appointment_id,
          available_date: input.available_date,
          existing_id: existing.id,
        })
        return existing as MonitoringBookingTask
      }

      const task = this.db.monitoring_booking_task
        ? await this.db.monitoring_booking_task.create({
            data: {
              monitor_id: input.monitor_id,
              appointment_id: input.appointment_id,
              available_date: input.available_date,
              office_code: input.office_code,
              service_type: input.service_type,
              priority: input.priority ?? 1,
              booking_mode: input.booking_mode ?? 'request',
              booking_options: input.booking_options ? JSON.stringify(input.booking_options) : null,
              max_retries: input.max_retries ?? 3,
              status: 'pending',
            },
          })
        : null

      if (!task) {
        throw new Error('Prisma 客户端不支持 monitoring_booking_task 操作，将回退到原生 SQL')
      }

      this.logger.info('创建监控预约任务', {
        task_id: task.id,
        appointment_id: input.appointment_id,
        available_date: input.available_date,
      })

      return task as MonitoringBookingTask
    }
    catch (error: any) {
      // Prisma在某些打包环境下可能出现 Invalid invocation，这里回退到原生SQL
      const msg = error?.message || String(error)
      this.logger.debug('创建监控预约任务失败，尝试使用原生SQL回退', { error: msg })
      try {
        // 1) 先查重
        const dupRows = await this.db.$queryRawUnsafe(
          'SELECT * FROM monitoring_booking_task WHERE appointment_id = ? AND available_date = ? AND status IN (\'pending\',\'processing\') LIMIT 1;',
          input.appointment_id,
          input.available_date,
        )
        if (Array.isArray(dupRows) && dupRows.length > 0)
          return dupRows[0] as MonitoringBookingTask

        // 2) 插入
        const bookingMode = input.booking_mode ?? 'request'
        const priority = Number.isFinite(input.priority as any) ? (input.priority as any) : 1
        const maxRetries = Number.isFinite(input.max_retries as any) ? (input.max_retries as any) : 3
        const bookingOptions = input.booking_options ? JSON.stringify(input.booking_options) : null

        await (this.db as any).$executeRawUnsafe(
          `INSERT INTO monitoring_booking_task (
            monitor_id, appointment_id, available_date, office_code, service_type,
            priority, status, booking_mode, booking_options, error_message,
            retry_count, max_retries, created_at, updated_at
          ) VALUES (?, ?, ?, ?, ?, ?, 'pending', ?, ?, NULL, 0, ?, CURRENT_TIMESTAMP, CURRENT_TIMESTAMP);`,
          input.monitor_id,
          input.appointment_id,
          input.available_date,
          input.office_code,
          input.service_type,
          priority,
          bookingMode,
          bookingOptions,
          maxRetries,
        )

        const rowIdRows = await this.db.$queryRawUnsafe('SELECT last_insert_rowid() AS id;')
        const newId = Number(rowIdRows?.[0]?.id || 0)
        if (Number.isFinite(newId) && newId > 0) {
          const rows = await this.db.$queryRawUnsafe(
            'SELECT * FROM monitoring_booking_task WHERE id = ? LIMIT 1;',
            newId,
          )
          if (Array.isArray(rows) && rows.length > 0)
            return rows[0] as MonitoringBookingTask
        }

        // 如果无法读取新行，则再次按唯一键查询
        const rows2 = await this.db.$queryRawUnsafe(
          'SELECT * FROM monitoring_booking_task WHERE appointment_id = ? AND available_date = ? ORDER BY id DESC LIMIT 1;',
          input.appointment_id,
          input.available_date,
        )
        if (Array.isArray(rows2) && rows2.length > 0)
          return rows2[0] as MonitoringBookingTask

        throw new Error('原生SQL插入任务后未找到记录')
      }
      catch (fallbackErr: any) {
        this.logger.error('创建监控预约任务失败（回退也失败）', {
          error: fallbackErr?.message || String(fallbackErr),
          input,
        })
        throw fallbackErr
      }
    }
  }

  /**
   * 批量创建监控预约任务
   */
  async createTasks(inputs: CreateMonitoringBookingTaskInput[]): Promise<MonitoringBookingTask[]> {
    const tasks: MonitoringBookingTask[] = []
    for (const input of inputs) {
      try {
        const task = await this.createTask(input)
        tasks.push(task)
      }
      catch (error: any) {
        this.logger.warn('批量创建任务失败', {
          error: error?.message || String(error),
          input,
        })
      }
    }
    return tasks as MonitoringBookingTask[]
  }

  /**
   * 获取待处理任务（按优先级排序）
   */
  async getPendingTasks(limit?: number): Promise<MonitoringBookingTask[]> {
    try {
      const tasks = this.db.monitoring_booking_task
        ? await this.db.monitoring_booking_task.findMany({
            where: {
              status: 'pending',
            },
            orderBy: [
              { priority: 'desc' },
              { created_at: 'asc' },
            ],
            take: limit,
          })
        : []

      return tasks as MonitoringBookingTask[]
    }
    catch (error: any) {
      // Prisma 失败时，尝试原生SQL回退
      const errorMsg = error?.message || String(error)
      this.logger.debug('获取待处理任务失败，尝试使用原生SQL回退', { error: errorMsg.substring(0, 200) })
      try {
        const hasLimit = typeof limit === 'number' && Number.isFinite(limit) && limit > 0
        const sql = `
          SELECT id, monitor_id, appointment_id, available_date, office_code, service_type,
                 priority, status, booking_mode, booking_options, error_message,
                 retry_count, max_retries, created_at, updated_at, started_at, completed_at
          FROM monitoring_booking_task
          WHERE status='pending' AND retry_count < max_retries
          ORDER BY priority DESC, created_at ASC${hasLimit ? ' LIMIT ?' : ''};
        `
        const rows = hasLimit
          ? await this.db.$queryRawUnsafe(sql, limit)
          : await this.db.$queryRawUnsafe(sql)
        return Array.isArray(rows) ? (rows as MonitoringBookingTask[]) : []
      }
      catch (rawErr: any) {
        this.logger.debug('原生SQL回退失败，返回空任务列表', { error: rawErr?.message || String(rawErr) })
        return []
      }
    }
  }

  /**
   * 标记任务为处理中
   */
  async markAsProcessing(taskId: number): Promise<void> {
    try {
      if (this.db.monitoring_booking_task) {
        await this.db.monitoring_booking_task.update({
          where: { id: taskId },
          data: {
            status: 'processing',
            started_at: new Date(),
          },
        })
        return
      }
    }
    catch {
      // 回退到原生SQL
    }
    try {
      await this.db.$executeRawUnsafe(
        'UPDATE monitoring_booking_task SET status=\'processing\', started_at=CURRENT_TIMESTAMP, updated_at=CURRENT_TIMESTAMP WHERE id=?;',
        taskId,
      )
    }
    catch (rawErr: any) {
      this.logger.error('标记任务为处理中失败', {
        error: rawErr?.message || String(rawErr),
        task_id: taskId,
      })
      throw rawErr
    }
  }

  /**
   * 标记任务为完成
   */
  async markAsCompleted(taskId: number): Promise<void> {
    try {
      if (this.db.monitoring_booking_task) {
        await this.db.monitoring_booking_task.update({
          where: { id: taskId },
          data: {
            status: 'completed',
            completed_at: new Date(),
          },
        })
        return
      }
    }
    catch {
      // 回退到原生SQL
    }
    try {
      await this.db.$executeRawUnsafe(
        'UPDATE monitoring_booking_task SET status=\'completed\', completed_at=CURRENT_TIMESTAMP, updated_at=CURRENT_TIMESTAMP WHERE id=?;',
        taskId,
      )
    }
    catch (rawErr: any) {
      this.logger.error('标记任务为完成失败', {
        error: rawErr?.message || String(rawErr),
        task_id: taskId,
      })
      throw rawErr
    }
  }

  /**
   * 标记任务为失败
   */
  async markAsFailed(taskId: number, errorMessage: string, shouldRetry: boolean = true): Promise<void> {
    try {
      if (this.db.monitoring_booking_task) {
        const task = await this.db.monitoring_booking_task.findUnique({
          where: { id: taskId },
        })

        if (!task)
          return

        const retryCount = task.retry_count + 1
        const shouldRetryTask = shouldRetry && retryCount < task.max_retries

        await this.db.monitoring_booking_task.update({
          where: { id: taskId },
          data: {
            status: shouldRetryTask ? 'pending' : 'failed',
            error_message: errorMessage,
            retry_count: retryCount,
            ...(shouldRetryTask ? {} : { completed_at: new Date() }),
          },
        })

        if (shouldRetryTask) {
          this.logger.info('任务将重试', {
            task_id: taskId,
            retry_count: retryCount,
            max_retries: task.max_retries,
          })
        }
        return
      }
    }
    catch {
      // 回退到原生SQL
    }
    try {
      const rows = await this.db.$queryRawUnsafe(
        'SELECT retry_count, max_retries FROM monitoring_booking_task WHERE id=? LIMIT 1;',
        taskId,
      )
      if (!Array.isArray(rows) || rows.length === 0)
        return

      const currentRetry = Number((rows[0] as { retry_count?: number })?.retry_count || 0)
      const maxRetries = Number((rows[0] as { max_retries?: number })?.max_retries || 3)
      const nextRetry = currentRetry + 1
      const retryable = shouldRetry && nextRetry < maxRetries

      if (retryable) {
        await this.db.$executeRawUnsafe(
          'UPDATE monitoring_booking_task SET status=\'pending\', error_message=?, retry_count=?, updated_at=CURRENT_TIMESTAMP WHERE id=?;',
          errorMessage,
          nextRetry,
          taskId,
        )
      }
      else {
        await this.db.$executeRawUnsafe(
          'UPDATE monitoring_booking_task SET status=\'failed\', error_message=?, retry_count=?, updated_at=CURRENT_TIMESTAMP, completed_at=CURRENT_TIMESTAMP WHERE id=?;',
          errorMessage,
          nextRetry,
          taskId,
        )
      }
    }
    catch (rawErr: any) {
      this.logger.error('标记任务为失败失败', {
        error: rawErr?.message || String(rawErr),
        task_id: taskId,
      })
      throw rawErr
    }
  }

  /**
   * 取消任务
   */
  async cancelTask(taskId: number): Promise<void> {
    try {
      if (this.db.monitoring_booking_task) {
        await this.db.monitoring_booking_task.update({
          where: { id: taskId },
          data: {
            status: 'cancelled',
            completed_at: new Date(),
          },
        })
        return
      }
    }
    catch {
      // 回退到原生SQL
    }
    try {
      await this.db.$executeRawUnsafe(
        'UPDATE monitoring_booking_task SET status=\'cancelled\', completed_at=CURRENT_TIMESTAMP, updated_at=CURRENT_TIMESTAMP WHERE id=?;',
        taskId,
      )
    }
    catch (rawErr: any) {
      this.logger.error('取消任务失败', {
        error: rawErr?.message || String(rawErr),
        task_id: taskId,
      })
      throw rawErr
    }
  }

  /**
   * 获取任务统计信息
   */
  async getStats(): Promise<{
    pending: number
    processing: number
    completed: number
    failed: number
    cancelled: number
  }> {
    try {
      if (this.db.monitoring_booking_task) {
        const [pending, processing, completed, failed, cancelled] = await Promise.all([
          this.db.monitoring_booking_task.count({ where: { status: 'pending' } }),
          this.db.monitoring_booking_task.count({ where: { status: 'processing' } }),
          this.db.monitoring_booking_task.count({ where: { status: 'completed' } }),
          this.db.monitoring_booking_task.count({ where: { status: 'failed' } }),
          this.db.monitoring_booking_task.count({ where: { status: 'cancelled' } }),
        ])

        return {
          pending,
          processing,
          completed,
          failed,
          cancelled,
        }
      }
    }
    catch {
      // 回退到原生SQL
    }
    try {
      const rows = await Promise.all([
        this.db.$queryRawUnsafe('SELECT COUNT(1) AS c FROM monitoring_booking_task WHERE status=\'pending\';'),
        this.db.$queryRawUnsafe('SELECT COUNT(1) AS c FROM monitoring_booking_task WHERE status=\'processing\';'),
        this.db.$queryRawUnsafe('SELECT COUNT(1) AS c FROM monitoring_booking_task WHERE status=\'completed\';'),
        this.db.$queryRawUnsafe('SELECT COUNT(1) AS c FROM monitoring_booking_task WHERE status=\'failed\';'),
        this.db.$queryRawUnsafe('SELECT COUNT(1) AS c FROM monitoring_booking_task WHERE status=\'cancelled\';'),
      ])
      const getC = (arr: unknown[]) => Number(((arr?.[0] as { c?: number }) || {})?.c || 0)
      return {
        pending: getC((rows[0] || []) as unknown[]),
        processing: getC((rows[1] || []) as unknown[]),
        completed: getC((rows[2] || []) as unknown[]),
        failed: getC((rows[3] || []) as unknown[]),
        cancelled: getC((rows[4] || []) as unknown[]),
      }
    }
    catch (rawErr: any) {
      this.logger.error('获取任务统计失败', {
        error: rawErr?.message || String(rawErr),
      })
      throw rawErr
    }
  }
}

let service: MonitoringBookingTaskService | null = null

export function getMonitoringBookingTaskService(): MonitoringBookingTaskService {
  if (!service)
    service = new MonitoringBookingTaskService()
  return service
}
